#include "stdafx.h"
#include "worldeditorapp.h"

#include <LitCore.h>

using namespace Lit;
WorldEditorApp::WorldEditorApp(QWidget *parent, Qt::WFlags flags)
	: QMainWindow(parent, flags)
{
	int *i = new int(20);
	boost::any pi(i);
	int *ppi = boost::any_cast<int*>( pi );
	if( ppi ) delete ppi;
        // Let's add a nice window icon
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
        HWND hwnd=winId();
        LONG iconID   = (LONG)LoadIcon( GetModuleHandle(0), MAKEINTRESOURCE(IDI_APPICON) );
        SetClassLong( hwnd, GCL_HICON, iconID );
#endif

	ui.setupUi(this);

	mViewport = new RenderViewport(this);
	
	mResources = new Lit::ResourcesList();
	mOgre = mViewport->ogreWidget();
	mOgre->addListener( this );
	mProperties = ui.PropertiesWidget;
	mPreview = ui.OgreSubPreview;
	mPhysicsObjectCreator = new PhysicsObjectCreator(mOgre, this);

	ui.OgreSubPreview->setOgreWidget( mOgre );
	ui.PropertiesWidget->setPreview( *mPreview );
	ui.PropertiesWidget->setViewport( *mViewport );
	setCentralWidget( *mViewport );

	connect( *mViewport, SIGNAL( renderMenuRequested( float, float ) ), this, SLOT( renderMenuRequested(float,float) ) );
	connect( *mViewport, SIGNAL( updateSceneSelection( const UserQueryResult& ) ), this, SLOT(updateSceneSelection(const UserQueryResult&)) );
	
	connect( *mViewport, SIGNAL( mousePressSignal( QMouseEvent* ) ), this, SLOT(renderMousePress(QMouseEvent*)) );
	connect( *mViewport, SIGNAL( mouseMoveSignal( QMouseEvent* ) ), this, SLOT(renderMouseMove(QMouseEvent*)) );
	connect( *mViewport, SIGNAL( mouseReleaseSignal( QMouseEvent* ) ), this, SLOT(renderMouseRelease(QMouseEvent*)) );

	connect( *mViewport, SIGNAL( keyPressSignal(QKeyEvent*) ), this, SLOT(renderKeyPress(QKeyEvent*)) );
	connect( *mViewport, SIGNAL( keyReleaseSignal(QKeyEvent*) ), this, SLOT(renderKeyRelease(QKeyEvent*)) );

	QMenu * m = new QMenu( this );
	QAction*actXZ = m->addAction( Lit::trPlaneAxis(Lit::PLANE_XZ) );
	QAction*actYZ = m->addAction( Lit::trPlaneAxis(Lit::PLANE_YZ) );
	QAction*actXY = m->addAction( Lit::trPlaneAxis(Lit::PLANE_XY) );
	
	connect( actXZ, SIGNAL(triggered(bool)), this, SLOT( actionMoveXZ(bool) ) );
	connect( actYZ, SIGNAL(triggered(bool)), this, SLOT( actionMoveYZ(bool) ) );
	connect( actXY, SIGNAL(triggered(bool)), this, SLOT( actionMoveXY(bool) ) );

	ui.actionMove->setMenu( m );
 
}

WorldEditorApp::~WorldEditorApp()
{	
	if( mPhysicsObjectCreator ) delete mPhysicsObjectCreator, mPhysicsObjectCreator = NULL;
	mSettings.destroy();
	mPlacer.destroy();
	mProject.destroy();
	mResources.destroy();
	mViewport.destroy();
	
}
void WorldEditorApp::actionMoveXZ(bool)
{
	mPlacer->setMovePlane( Lit::PLANE_XZ );
	activateMoveTool( true );
}
void WorldEditorApp::actionMoveXY(bool)
{
	mPlacer->setMovePlane( Lit::PLANE_XY );
	activateMoveTool( true );
}
void WorldEditorApp::actionMoveYZ(bool)
{
	mPlacer->setMovePlane( Lit::PLANE_YZ );
	activateMoveTool( true );
}
void WorldEditorApp::updateUi( )
{
	ui.actionAdd_To_Simulation->setEnabled( !Lit::Root::SceneManager()->getSelection()->empty() );
}
void WorldEditorApp::updateSceneSelection( const UserQueryResult& res )
{
	if( mPlacer->isPlacing() ) return;
	Lit::SceneManagerPtr mgr = Lit::Root::SceneManager();
	if( !(GetAsyncKeyState(VK_CONTROL) & 0x8000) && !(GetAsyncKeyState(VK_LMENU) & 0x8000) ) 
		mgr->clearSelection();
	if( res.hit )
	{
		if( res.volume )
		{
			for( auto i=res.contacts.begin();
				i!=res.contacts.end();
				i++ )
			{
				mgr->setSelected( mgr->getMovable( i->movable->getName() ), true );
			}
		}
		else{
			Ogre::MovableObject * obj = res.firstContact().movable;
			Lit::MovablePtr mov = mgr->getMovable( obj->getName() );
			if( mgr->getSelection()->isSelected( mov ) && (GetAsyncKeyState(VK_LMENU) & 0x8000) )
			{
				mgr->setSelected( mov, false );
			}
			else mgr->setSelected( mov, !mgr->getSelection()->isSelected( mov ) );
		}
	}
	updateUi();
}


void WorldEditorApp::mouseDragging(  MouseDrag & drag  )
{
	
}
void WorldEditorApp::keyPressEvent( QKeyEvent*evt ){
	if( evt->modifiers().testFlag( Qt::ControlModifier ) && evt->key() == Qt::Key_C )
	{
		Lit::SceneSelectionPtr sel = Lit::Root::SceneManager()->getSelection();
		if( sel->empty() ) return;
		std::vector<Lit::MovablePtr> newsel;
		for( auto i=sel->begin(); i!=sel->end(); i++ )
		{
			ProjectNodePtr n = mProject->findNode( i->get() );
			std::string name = (n->type() == PNT_ENTITY ? ((Lit::Entity*)*n->movable())->getMeshName() : ((Lit::Light*)*n->movable())->typestr());
			newsel.push_back( mProject->createMovable( name, n->type() )->movable() );
			newsel.back()->setPosition( i->get()->getPosition() );
		}
		sel->clearSelection();
		for( auto j=newsel.begin(); j!=newsel.end(); j++ )
			sel->setSelected( *j, true );
		mPlacer->setPlacingMode( true );
	}
}
void WorldEditorApp::renderKeyRelease( QKeyEvent * evt ){
}
void WorldEditorApp::renderMousePress( QMouseEvent*evt ){
}
void WorldEditorApp::renderMouseMove( QMouseEvent * evt ){
}
void WorldEditorApp::renderMouseRelease( QMouseEvent * evt ){
	if( mPlacer->isPlacing() && evt->button() == Qt::LeftButton )
	{
		activateMoveTool( false );
	}
}
void WorldEditorApp::activateMoveTool( bool state ){
		mPlacer->setPlacingMode( state );
		ui.actionMove->setChecked( state );
		if( state ) ui.actionRotate->setChecked( false );
		if( state ) ui.actionScale->setChecked( false );
		ui.actionMove->setText( tr( "Move" )+"( "+QString(Lit::trPlaneAxis(mPlacer->movingPlane()))+")" );
}
void WorldEditorApp::activateRotateTool( bool state ){
	ui.actionRotate->setChecked( state );
	if( state ) ui.actionMove->setChecked( false );
	if( state ) ui.actionScale->setChecked( false );
}
void WorldEditorApp::activateScaleTool( bool state ){
	ui.actionScale->setChecked( state );
	if( state ) ui.actionRotate->setChecked( false );
	if( state ) ui.actionMove->setChecked( false );
}

void WorldEditorApp::createProject( const std::string &name )
{
	mProject.destroy();
	Lit::Root::SceneManager()->clearScene();
	if( name.empty() )
		mProject = new Project( ui.ProjectDockContent, ui.OgreSubPreview, ui.PropertiesWidget, mResources );
	else 
		mProject = new Project( name, ui.ProjectDockContent, ui.OgreSubPreview, ui.PropertiesWidget, mResources );
	mProperties->setProject( *mProject );
}

void WorldEditorApp::saveProjectAs( )
{
	if( !mProject ) return;
	mProject->save();
}
void WorldEditorApp::saveProject()
{
	if( !mProject ) return;
	mViewport->blockSignals( true );
	mProject->save();
	mViewport->blockSignals( false );
}
void WorldEditorApp::openProject()
{
	if( !mProject ) return;
	mProject->open();
}
void WorldEditorApp::createScene( ){
#ifndef NDEBUG
	Root::Instance().initialise( 
		mOgre->ogreRenderWindow(), mOgre->ogreCamera(), mOgre->ogreSceneManager(), 
		CoreComponentList() << 
			Lit::COM_CACHEMANAGER <<
			Lit::COM_SCENEMANAGER << 
			Lit::COM_PHYSICSMANAGER ,
		"WorldEditor_d.log",
		false
	);
#else
	Root::Instance().initialise( 
		mOgre->ogreRenderWindow(), mOgre->ogreCamera(), mOgre->ogreSceneManager(), 
		CoreComponentList() << 
			Lit::COM_CACHEMANAGER <<
			Lit::COM_SCENEMANAGER << 
			Lit::COM_PHYSICSMANAGER ,
		"WorldEditor.log",
		false
	);
#endif

	mLog = *Root::Instance().log();
	mLog->addListener( this );
	Ogre::LogManager::getSingleton().getDefaultLog()->addListener( this );

	listResources( );
	
	ui.MaterialSearch->setupBuddies( *mResources, ui.MaterialList );
	ui.ModelSearch->setupBuddies( *mResources, ui.ModelList );
	ui.CompositorSearch->setupBuddies( *mResources, ui.CompositorList );
	ui.TextureSearch->setupBuddies( *mResources, ui.TextureList );
	
	mSettings = new EditorSettings(this);
	mPlacer = new MovablePlacer(mViewport);

	createProject("Unnamed");

	//on_actionImport_Scene_triggered(true);
	mLog->logMessage("Editor Initialised");
	
	Ogre::MeshPtr floorMesh = Ogre::MeshManager::getSingleton().createPlane( 
		"floorMesh", 
		Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 
		Ogre::Plane( Lit::Vec3::UNIT_Y, 0 ), 
		1000, 1000, 
		10, 10, 
		true, 1, 
		10, 10, 
		Lit::Vec3::UNIT_X 
	);
	Lit::EntityPtr floor = Lit::Root::SceneManager()->createEntity( "floorMesh" );
	floor->setMaterialName("Examples/Rockwall");
    floor->getEntity()->setCastShadows(false);

	mWorld = Lit::Root::PhysicsManager()->createWorld( Lit::PhysicsManager::DEFAULT_WORLD_NAME, Lit::Physics::PO_NONE, Lit::Physics::SM_ITERATIVE_SIMPLE, Lit::AABB( Lit::Vec3( -500.0f ), Lit::Vec3( 500.0f ) ) );
	Lit::Physics::BodyPtr body(mWorld->createStaticBody( *floor, Lit::Physics::ST_PLANE, 0 ));
	Lit::Vec3 camdir(mOgre->ogreCamera()->getDirection().normalisedCopy());
	Lit::Vec3 campos( mOgre->ogreCamera()->getPosition() );
	/*
	Lit::EntityPtr ent = Lit::Root::SceneManager()->createEntity( "sphere.mesh" );
	Physics::BodyPtr ball = Lit::Root::PhysicsManager()->getWorld()->createBody( 50, *ent );
	ball->getEntity()->setMaterialName( "Examples/SphereMappedRustySteel" );
	ball->addForces( Physics::Force( camdir, Lit::Vec3( 100.0f ), Lit::Vec3( 800.0f ) ) );*/
}
bool WorldEditorApp::frameRenderingQueued( const Ogre::FrameEvent& evt )
{
	mLastFrameTime = evt.timeSinceLastFrame;
	mPlacer->update( );
	if( mViewport->mouseDrag()->rubberBand() ) 
		mViewport->mouseDrag()->rubberBand()->setVisible( mViewport->mouseDrag()->isDragging() && (!mPlacer->isPlacing()) );
	Lit::Root::Instance().renderOneFrame( evt.timeSinceLastFrame );
	
	static Lit::Real spaceAccu = evt.timeSinceLastFrame;
	static Lit::Real fpsTAccu = 0.0f;
	if( GetAsyncKeyState(VK_SPACE)&0x8000 && spaceAccu >= 0.150 )//150ms
	{
		Lit::Vec3 camdir(mOgre->ogreCamera()->getDirection().normalisedCopy());
		Lit::Vec3 campos( mOgre->ogreCamera()->getPosition() );
		
		Lit::Vec3 scal(0.01f);
		Lit::Vec3 pos( campos + camdir*50.0f);
		Physics::Force f(camdir,100.0f,100.0f, 800.0f);

		Physics::BodyPtr ball = Lit::Root::SceneManager()->createDynamicBody( mWorld, "geosphere4500.mesh", 100000, Lit::Physics::ST_SPHERE, NULL, NULL, pos, scal );
		ball->getEntity()->setMaterialName( "Examples/SphereMappedRustySteel" );
		ball->addExternalForce( f );
		spaceAccu = 0.0f;
	}
	else spaceAccu += evt.timeSinceLastFrame;
	fpsTAccu += evt.timeSinceLastFrame;
	if( fpsTAccu >= 1.0f ) setWindowTitle( QString("%1").arg(mOgre->ogreRenderWindow()->getLastFPS()) );
	return true;
}

void WorldEditorApp::destroyScene( ){Lit::Root::Instance().shutdown();}


void WorldEditorApp::on_actionAdd_To_Simulation_triggered(bool)
{
	if( !Lit::Root::SceneManager()->getSelection()->empty() )
	{
		Lit::MovablePtr mov (Lit::Root::SceneManager()->getSelection()->list().at(0));
		if( mov->getMovableType() == Lit::MT_ENTITY )
			mPhysicsObjectCreator->exec(((Lit::Entity*)*mov));
	}
}
void WorldEditorApp::on_actionVisual_Debugger_triggered( bool state ){mWorld->getDebugger()->setVisible(true);}
void WorldEditorApp::on_actionRun_triggered(bool state){	Lit::Root::PhysicsManager()->setPaused(!state);}
void WorldEditorApp::on_actionReset_triggered(bool){	Lit::Root::PhysicsManager()->setPaused( true );	ui.actionRun->setChecked( false );	mWorld->resetWorld();}
void WorldEditorApp::on_actionSettings_triggered(bool){	mSettings->execute( ESP_GENERAL );}

void WorldEditorApp::on_actionNew_Scene_triggered(bool){ createProject(); }
void WorldEditorApp::on_actionOpen_Scene_triggered(bool){ openProject(); }
void WorldEditorApp::on_actionSave_Scene_triggered(bool){ saveProject(); }
void WorldEditorApp::on_actionSave_Scene_As_triggered(bool){saveProjectAs();}
void WorldEditorApp::on_actionClose_Scene_triggered(bool){ mProject->close(); }
void WorldEditorApp::on_actionExit_triggered( bool ){	qApp->quit();}

void WorldEditorApp::on_actionMove_triggered(bool state){ activateMoveTool(state); }
void WorldEditorApp::on_actionRotate_triggered(bool state){ activateRotateTool(state); }
void WorldEditorApp::on_actionScale_triggered(bool state){ activateScaleTool(state); }

/************* RESOURCES TABS *****************/
void WorldEditorApp::on_ModelList_itemDoubleClicked( QListWidgetItem * i ){
	EDT_TRY
	{
		Lit::MovablePtr mov=mProject->createMovable( i->text().toAscii().data(), PNT_ENTITY )->movable();
		Lit::Root::SceneManager()->getSelection()->setSingleSelection( mov );
		activateMoveTool( true );
	}
	EDT_CATCH( this );
}
void WorldEditorApp::on_LightsList_itemDoubleClicked( QListWidgetItem* i )
{
	Lit::Root::SceneManager()->clearSelection(true);
	Lit::MovablePtr mov;
	Lit::SceneManagerPtr mgr = Lit::Root::Instance().SceneManager();
	EDT_TRY
	{
		mov=mProject->createMovable( i->text().toAscii().data(), PNT_LIGHT )->movable();
		std::string tname = i->text().toAscii().data();
		if( mov->hasData("EntityBuddy") ){
			Lit::EntityPtr ent(Lit::Root::SceneManager()->getEntity(mov->getData("EntityBuddy", std::string())));
			mProperties->updateProperties( mProject->findNode( ((Lit::Movable*)*ent) ) );
			Lit::Root::SceneManager()->getSelection()->setSingleSelection( ((Lit::Movable*)*ent) );
		}
		activateMoveTool( true );
	}
	EDT_CATCH( this );
}

void WorldEditorApp::on_MaterialList_itemClicked( QListWidgetItem * sel ){
	std::string mat = sel->text().toAscii().data();
	if( !Lit::Root::SceneManager()->getSelection()->empty() )
	{
		for( auto i=Lit::Root::SceneManager()->getSelection()->begin(); i!=Lit::Root::SceneManager()->getSelection()->end(); i++ )
			if( i->get()->getMovableType() == Lit::MT_ENTITY )
				((Lit::Entity*)(i->get()))->setMaterialName( mat );
	}
	else EXC_GUARD( this, mPreview->getEntity()->setMaterialName( mat ), "Cannot change material");
}

void WorldEditorApp::on_ModelList_itemClicked( QListWidgetItem * itm )
{
	EXC_GUARD( this, mPreview->replaceMesh( itm->text().toAscii().data() ), "Cannot change model");
}

void WorldEditorApp::on_CompositorEnable_clicked( bool state)
{
}


void WorldEditorApp::renderMenuRequested(float dx, float dy)
{
	Lit::StringList lst;
	Lit::SceneManagerPtr mgr( Lit::Root::SceneManager() );
	Lit::SceneSelectionPtr sel = mgr->getSelection();
	QString selstr = Lit::String("("+Lit::String(sel?(sel->isSingleSelection()?sel->singleSelection()->getName():Lit::String(sel->size())):"error")(" objects")+")").cstr();
	mViewport->menu()->clear();
	QAction * sall = mViewport->menu()->addAction( QIcon(), "Select All" );
	QAction * snon = mViewport->menu()->addAction( QIcon(), "Select None" );
	QAction * sinv = mViewport->menu()->addAction( QIcon(), "Select Inverse" );
	mViewport->menu()->addSeparator();
	
	QAction * hsel = mViewport->menu()->addAction( QIcon(), "Hide Selected "+selstr );
	if( sel->empty() )  hsel->setEnabled( false );
	QAction * hunsel = mViewport->menu()->addAction( QIcon(), "Hide UnSelected" );

	QAction * rmsel = mViewport->menu()->addAction( QIcon(), "Remove Selected "+selstr );
	if( sel->empty() )  hsel->setEnabled( false );
	
	QAction * hall = mViewport->menu()->addAction( QIcon(), "Hide All" );
	mViewport->menu()->addSeparator();
	
	QAction * unhall = mViewport->menu()->addAction( QIcon(), "UnHide All" );
	mViewport->menu()->addSeparator();
	
	QAction * objProps = mViewport->menu()->addAction( QIcon(), "Object Properties "+selstr );
	if( sel->empty() ) objProps->setEnabled(false);

	QAction *exRet = mViewport->menu()->exec( QCursor::pos());
	if( !exRet ) return;
	if( exRet == sall )
	{
		mgr->selectAll();
	}
	else if( exRet == snon )
	{
		mgr->selectNone();
	}
	else if( exRet == sinv )
	{
		mgr->selectInverse();
	}
	else if( exRet == rmsel )
	{
		if( sel->isSingleSelection() )
		{
			Lit::MovablePtr mov(sel->singleSelection());
			sel->clearSelection(true);
			if( mov )
				mProject->destroyNode( mProject->findNode(mov) );
		}
		else
		{
			MovablePtrList lst(sel->list());
			sel->clearSelection(true);
			for( auto i=lst.begin(); i!=lst.end(); i++ )
			{
				Lit::MovablePtr m((*i));
				if( m )
				{
					mProject->destroyNode( mProject->findNode(m) );
				}
			}
		}
	}
	else if( exRet == hsel )
	{
		sel->setSelectionVisible( false );
	}
	else if( exRet == hunsel )
	{
		mgr->selectInverse();
		sel->setSelectionVisible( false );
	}
	else if( exRet == hall )
	{
		mgr->selectAll();
		sel->setSelectionVisible( false );
	}
	else if( exRet == unhall )
	{
		mgr->selectAll();
		sel->setSelectionVisible( true );
	}
	else if( exRet == objProps )
	{
	}
}

void WorldEditorApp::listResources( )
{
	ui.TextureList->clear();
	ui.MaterialList->clear();
	ui.ModelList->clear();
	ui.LightsList->clear();
	ui.CompositorList->clear();
	ui.ResLocTree->clear();
	ui.LightsList->addItem( Lit::trLightType( Lit::LT_POINT ) );
	ui.LightsList->addItem( Lit::trLightType( Lit::LT_DIRECTIONAL ) );
	ui.LightsList->addItem( Lit::trLightType( Lit::LT_SPOTLIGHT ) );
	mResources->updateLists();

	std::map<std::string, QTreeWidgetItem*> map;
	for( auto i=mResources->locations().begin(); i!=mResources->locations().end(); i++ )
	{
		if( map.find( i->group() ) == map.end() ){
			map[i->group()] = new QTreeWidgetItem(ui.ResLocTree, QStringList((i->group()).c_str()));
			ui.ResLocTree->addTopLevelItem( map[i->group()] );
		}
		new QTreeWidgetItem(map[i->group()], QStringList((i->path()).c_str()));
	}
	for( auto i=mResources->textures().begin(); i!=mResources->textures().end(); i++ ) ui.TextureList->addItem( i->getName().c_str() );
	for( auto i=mResources->materials().begin(); i!=mResources->materials().end(); i++ ) ui.MaterialList->addItem( i->getName().c_str() );
	for( auto i=mResources->meshes().begin(); i!=mResources->meshes().end(); i++ ) ui.ModelList->addItem( i->getName().c_str() );
	for( auto i=mResources->compositors().begin(); i!=mResources->compositors().end(); i++ )
	{
		Ogre::CompositorManager::getSingleton().addCompositor(mViewport->ogreWidget()->ogreViewport(), i->getName());
		ui.CompositorList->addItem( i->getName().c_str() );
		mCompositors[i->getName()] = false;
	}
}

void WorldEditorApp::messageLogged( const Ogre::String& message, Ogre::LogMessageLevel lml, bool maskDebug, const Ogre::String &logName ){
	QString tstamp(QTime::currentTime().toString("HH:mm:ss"));
	static const int max_lines = 100;
	if( ui.logEdit->toPlainText().size() > 255*max_lines ){
		std::vector<Lit::String> ex = Lit::String(ui.logEdit->toPlainText().toAscii().data()).tokenise("\n");
		ex.erase( ex.begin() +(max_lines/2), ex.end() );
		Lit::String str; for( auto i=ex.begin(); i!=ex.end(); i++ ) str+= (*i) + "\r\n";
		ui.logEdit->setPlainText( str.cstr() );
		ui.logEdit->verticalScrollBar()->scroll(0,ui.logEdit->verticalScrollBar()->height());
	}
	ui.logEdit->appendPlainText( "["+tstamp+"]: "+message.c_str() );
}